package edu.nf.llmtest.service.impl;

import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.nf.llmtest.common.converter.EssayDTOConverter;
import edu.nf.llmtest.common.enumeration.RedisKeyEnum;
import edu.nf.llmtest.mapper.EssayMapper;
import edu.nf.llmtest.model.document.EssayDoc;
import edu.nf.llmtest.model.dto.EssayDTO;
import edu.nf.llmtest.model.entity.Essay;
import edu.nf.llmtest.service.EssayService;
import edu.nf.llmtest.service.Listenter.MqProducer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**
 * @ Author bin
 * @ Create  2025/11/7 11:31
 **/
@RequiredArgsConstructor
@Service
@Slf4j
public class EssayServiceImpl implements EssayService {
    private final EssayMapper essayMapper;
    private final RedisTemplate<String, Object> redisTemplate;
    private final ElasticsearchOperations operations;
    private final EssayDTOConverter essayDTOConverter;
    private final MqProducer mqProducer;
    /**
     * 同步文章Redis和Elasticsearch中
     * 同时把数据放进Redis和Elasticsearch中还是Qdrant向量数据库中
     * */
    @Override
    public List<EssayDTO> listEssay() {
        List<EssayDTO> essayDTOS = essayMapper.listEssay();

        for (EssayDTO essayDTO : essayDTOS) {
            redisTemplate.opsForValue().set(RedisKeyEnum.ESSAY_PERFIX.getValue() + essayDTO.getEId(), essayDTO);
            operations.save(essayDTOConverter.convertEssayDTODoc(essayDTO));
        }
        return essayDTOS;
    }

    @Override
    public Integer getLikeCount(Integer eId){
        // 从redis中获取点赞数量
        Integer likeCount = (Integer)redisTemplate.opsForValue().get(RedisKeyEnum.ESSAY_LIKE_PERFIX.getValue() + eId);

        if (likeCount != null)
            return likeCount;
        likeCount = essayMapper.getLikeCount(eId);
        redisTemplate.opsForValue().set(RedisKeyEnum.ESSAY_LIKE_PERFIX.getValue() + eId, likeCount);
        return likeCount;
    }

    @Override
    public  List<EssayDTO> listEssay(List<String> eIds, Integer size, String type,String search){
        // 从ES获取文章
        List<EssayDTO> essays = listEssayFromEs(eIds, size, type, search);
        
        // 如果ES中没有数据，从数据库中获取
        if (essays == null) {
            essayMapper.listEssay();
        }
        essays = listEssayFromEs(eIds, size, type, search);
        if (essays == null)
            return null;
        
        // 从Redis中获取每篇文章的点赞数量并设置
        for (EssayDTO essay : essays) {
            Integer likeCount = getLikeCount(essay.getEId());
            essay.setLikeCount(likeCount);
        }

        log.info("文章最后获取结果{},有{}篇文章", essays,essays.size());
        return essays;
    }

    @Override
    public List<EssayDTO> listEssayFromEs(List<String> eIds, Integer size, String type, String search) {
        List<EssayDTO> essayDTOS = new ArrayList<>();
        NativeQueryBuilder queryBuilder = NativeQuery.builder();
        queryBuilder.withPageable(PageRequest.of(0, size));
        //构建高亮字段
        HighlightField title = new HighlightField("title");
        HighlightField content = new HighlightField("content");
        HighlightField userName = new HighlightField("user.userName");
        Highlight highlight = new Highlight(List.of(title,content,userName));
        //执行高亮查询
        // 搜索 类型、标题、内容、作者名字模糊搜索 排除已获取的文章id
        queryBuilder.withQuery(query -> {
            BoolQuery.Builder boolBuilder = new BoolQuery.Builder();

            if (StringUtils.hasText(search)) {
                BoolQuery.Builder searchBool = new BoolQuery.Builder();
                searchBool
                        .should(QueryBuilders.matchPhrase(term -> term.field("title").query(search)))
                        .should(QueryBuilders.matchPhrase(term -> term.field("content").query(search)))
                        .should(QueryBuilders.matchPhrase(term -> term.field("user.userName").query(search)));

                // 把搜索条件放到 must 中
                boolBuilder.must(searchBool.build());
            }

            if (StringUtils.hasText(type)) {
                boolBuilder.must(QueryBuilders.matchPhrase(term -> term.field("types").query(type)));
            }

            if (!CollectionUtils.isEmpty(eIds)) {
                eIds.forEach(eId -> {
                    boolBuilder.mustNot(QueryBuilders.term(term -> term.field("eId").value(eId)));
                });
            }

            boolean noCondition = !StringUtils.hasText(search)
                    && !StringUtils.hasText(type)
                    && CollectionUtils.isEmpty(eIds);

            if (noCondition) {
                return query.matchAll(m -> m);
            }

            // --- 返回组合后的 bool 查询 ---
            return query.bool(boolBuilder.build());
        }).withHighlightQuery(new HighlightQuery(highlight, EssayDoc.class));

        log.info("查询条件：{}", queryBuilder.getQuery());
        SearchHits<EssayDoc> hits = operations.search(queryBuilder.build(), EssayDoc.class);
        if (!hits.hasSearchHits()) {
            log.warn("没有查询到任何结果");
            return null;
        }
        log.info("查询结果：{}", hits);

        hits.getSearchHits().forEach(hit -> {
            List<String> titleHighlights = hit.getHighlightField("title");
            if (!CollectionUtils.isEmpty(titleHighlights)) {
                hit.getContent().setTitle(titleHighlights.getFirst());
            }

            List<String> contentHighlights = hit.getHighlightField("content");
            if (!CollectionUtils.isEmpty(contentHighlights)) {
                hit.getContent().setContent(contentHighlights.getFirst());
            }

            List<String> userNameHighlights = hit.getHighlightField("user.userName");
            if (!CollectionUtils.isEmpty(userNameHighlights)) {
                if (hit.getContent().getUser() != null) {
                    hit.getContent().getUser().setUserName(userNameHighlights.getFirst());
                }
            }
            EssayDTO essayDTO = essayDTOConverter.convertEssayDTO(hit.getContent());
            essayDTOS.add(essayDTO);
        });
        log.info("es搜索有{}篇文章",essayDTOS.size());
        return essayDTOS;
    }

    @Override
    public EssayDTO getEssayById(Integer eId) {
        EssayDTO essayDTO = (EssayDTO)redisTemplate.opsForValue().get(RedisKeyEnum.ESSAY_PERFIX.getValue() + eId);
        if (essayDTO == null){
            essayDTO = operations.get(eId.toString(), EssayDTO.class);
            if (essayDTO == null)
            {
                essayDTO = essayMapper.getEssayById(eId);
                operations.save(essayDTOConverter.convertEssayDTODoc(essayDTO));
                redisTemplate.opsForValue().set(RedisKeyEnum.ESSAY_PERFIX.getValue() + eId, essayDTO);

                return essayDTO;
            }
        }
        essayDTO.setLikeCount(getLikeCount(eId));
        return essayDTO;
    }

    @Override
    public void addEssay(Essay essay) {
        mqProducer.send("addEssayHanlder", essay);
    }
}
